#include "NezhaCReadFile.h"
#include "NezhaCFileSystem.h"

using namespace Nezha;
using namespace stream;

#if defined(NZOS_WIN32)
void WINAPI CReadFile::asyncReadCallback(DWORD error, DWORD transfered, LPOVERLAPPED overlapped) 
{
	SAsyncReadDesc* info = static_cast<SAsyncReadDesc*>(overlapped->hEvent);

	// Ok, tell the app we're done reading
	info->Readfile->bReading = false;
	info->Callback(transfered, error, info->Readfile, info->Arg);

	delete info;
}
#elif defined(NZOS_IOS)

struct sigaction CReadFile::OriginalSigAction;
std::queue<CReadFile::SAsyncReadDesc*> CReadFile::AioReadInQueue;
CReadFile::SAsyncReadDesc* CReadFile::AioReadInProgress = NULL;

void
CReadFile::aioSignalDone(int sig)
{
	SAsyncReadDesc* info = AioReadInProgress;
	// Get the result of our read
	int result = aio_return(&info->AioStruct);
	int error = aio_error(&info->AioStruct);

	// Ok, tell the app we're done reading
	info->Readfile->bReading = false;
	info->Callback(result, error, info->Readfile, info->Arg);
	delete info;
	
    // If there is no more read in queue, restore old signal handler
    if(AioReadInQueue.empty())
    {
		AioReadInProgress = NULL;
	    // Restor to old signal handler
	    sigaction(sig, &OriginalSigAction, 0);
    }
	else
	{
		AioReadInProgress = AioReadInQueue.front();
		AioReadInQueue.pop();
	}
}
#endif

CReadFile::CReadFile(const char* fileName, bool headerObfuscated)
    : mFilePtr(0)
    , mFileSize(0)
    , mFilename(fileName)
	//, mHeaderObfuscated(headerObfuscated)
{
	openFile();

	/**
	if(mHeaderObfuscated && mFileSize < 4)
	{
		mHeaderObfuscated = false;
	}
	**/
}

CReadFile::~CReadFile()
{
}


IReadFile*
CReadFile::clone(bool keepPos) const
{
	CReadFile* rf = NZ_New CReadFile(this->getFullPath());
   // rf->mHeaderObfuscated = mHeaderObfuscated;
	if(keepPos)
	{
		rf->seek(this->getPos());
	}
	return rf;
}

int
CReadFile::read(void* buffer, u32 sizeToRead)
{
	if(!isOpen())
	{
		return 0;
	}

	long pos = getPos();
	int result = (int)fread(buffer, 1, sizeToRead, mFilePtr->mFile);
	
	/**
	if(mHeaderObfuscated && pos < 4)
	{
		for(int i = 0, end = std::min<u32>(4-pos,sizeToRead); i < end; ++i)
		{
			reinterpret_cast<u8*>(buffer)[i] -= int(pos)+i+1;
		}
	}
	**/
	return result;
}

bool
CReadFile::readAsync(void* buffer, u32 sizeToRead, Callback callback, void* arg)
{
	return readAsync(buffer, sizeToRead, getPos(), callback, arg);
}

bool
CReadFile::readAsync(void* buffer, u32 sizeToRead, long finalPos, Callback callback, void* arg)
{
#if defined(NZOS_WIN32)

	SAsyncReadDesc* info = NZ_New SAsyncReadDesc;
	info->Readfile = this;
	info->Callback = callback;
	info->Arg = arg;
    
    // Create a WinAPI file handle as windows does not support async read with FILE*
    info->FileHandle = CreateFile(mFilePtr->mFullPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    // Initialize teh "overlapped" structure 
    memset(&info->Overlapped, 0, sizeof(OVERLAPPED));
    info->Overlapped.hEvent = info;
    info->Overlapped.Offset = finalPos;
    
    bReading = true;

	return ReadFileEx(info->FileHandle, buffer, sizeToRead, &info->Overlapped, asyncReadCallback) == TRUE ? true : false;

#elif defined(NZOS_IOS)
	
	int signalId = SIGIO;

	SAsyncReadDesc* info = NZ_New SAsyncReadDesc;
	info->Readfile = this;
	info->Callback = callback;
	info->Arg = arg;
	info->AioStruct.aio_fildes = fileno(mFilePtr->mFile);
	info->AioStruct.aio_offset = finalPos;
	info->AioStruct.aio_buf = buffer;
	info->AioStruct.aio_nbytes = sizeToRead;
	info->AioStruct.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
	info->AioStruct.aio_sigevent.sigev_signo = signalId;
	
	// Ok, indicate we currently have a background reading task
	bReading = true;
	
	if(AioReadInProgress)
	{
		// Temporarly block all signals to be sure another read don't mess our list
		sigset_t set, oldSet;
		sigemptyset(&set);
		sigaddset(&set,signalId);
		sigprocmask(SIG_BLOCK, &set, &oldSet);
		AioReadInQueue.push(info);
		sigprocmask(SIG_SETMASK, &oldSet, 0);
		return true;
	}
    else
    {
	    AioReadInProgress = info;
    	
	    // Set our signal handler
	    struct sigaction newSigAction;
	    memset(&newSigAction, 0, sizeof(struct sigaction));
	    newSigAction.sa_handler = aioSignalDone;
	    int result = sigaction(signalId, &newSigAction, &OriginalSigAction);
    	
        result = aio_read(&(info->AioStruct));

	    return result == 0;
    }
#endif

}

bool
CReadFile::seek(long finalPos, bool relativeMovement)
{
	if(!isOpen())
	{
		return false;
	}
        
	return fseek(mFilePtr->mFile, finalPos, relativeMovement ? SEEK_CUR : SEEK_SET) == 0;
}

long
CReadFile::getSize() const
{
	return mFileSize;
}

bool CReadFile::isOpen() const
{
	return mFilePtr != 0;
}

long
CReadFile::getPos() const
{
	return ftell(mFilePtr->mFile);
}

void
CReadFile::openFile()
{
	if(mFilename.size() == 0) 
	{
		return;
	}

	mFilePtr = CFileSystem::open(mFilename.c_str(), "rb");	

	if(mFilePtr)
	{
		// get FileSize
		fseek(mFilePtr->mFile, 0, SEEK_END);
		mFileSize = getPos();
		fseek(mFilePtr->mFile, 0, SEEK_SET);

		//mHeaderObfuscated = mFilePtr->HeaderObfuscated;
	}
}

const char*
CReadFile::getFileName() const
{
	return mFilename.c_str();
}

const char*
CReadFile::getFullPath() const
{
	return mFilePtr->mFullPath.c_str();
}

IReadFile*
	InternalCreateReadFile(const char* fileName)
{
	CReadFile* file = NZ_New CReadFile(fileName);
	if(file->isOpen())
	{
		return file;
	}
	//file->drop();
	return 0;
}